﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;

namespace Beeflys.Generator
{
    public sealed class UTypeInfo
    {
        public UTypeInfo(List<string> usings, ITypeSymbol typeSymbol) : this(typeSymbol, true)
        {
            this.Usings = usings;
        }

        public UTypeInfo(ITypeSymbol typeSymbol, bool isSource = false) : this(isSource)
        {
            this.sourceTypeSymbol = typeSymbol;
            
            // typeSymbol.Locations
        }

        private UTypeInfo(bool isSource)
        {
            this.IsSource = isSource;
        }

        public string Name { get; private set; }
        public string FullName { get; private set; }
        public bool IsSource { get; private set; }
        public bool IsSealed { get; private set; }
        public bool IsStatic { get; private set; }
        public bool IsAbstract { get; private set; }
        public bool IsTypeError { get; private set; }
        
        //修饰符
        public Accessibility Modifiers { get; private set; }

        /// <summary>
        /// 是否是泛型类
        /// </summary>
        public bool IsGenericType { get; private set; }
        
        /// <summary>
        /// 自己本身是泛型
        /// </summary>
        public bool IsSelfGenericType { get; private set; }
        public bool IsInterface { get; private set; }
        public bool IsTupleType { get; private set; }

        public List<string> Usings { get; private set; }

        public List<UPropertyInfo> Propertys { get; private set; } = new List<UPropertyInfo>();
        public List<UMethodInfo> Methods { get; private set; } = new List<UMethodInfo>();
        public List<UTypeInfo> Attributes { get; private set; } = new List<UTypeInfo>();


        private List<ImplInterfaceWrap> baseInterfaceWraps { get; set; } = new List<ImplInterfaceWrap>();
        private List<INamedTypeSymbol> allInterfaceSymbols { get; set; } = new List<INamedTypeSymbol>();

        private List<ITypeSymbol> GenericArgumentSymbols { get; set; } = new List<ITypeSymbol>();
        private ITypeSymbol GenericOriginalTypeSymbol { get; set; }

        private ITypeSymbol sourceTypeSymbol;

        public UTypeInfo BaseType { get; private set; }

        private List<UTypeInfo> baseInterfaces;
        private List<UTypeInfo> allInterfaces;
        
        private List<UTypeInfo> genericArguments;
        private UTypeInfo genericOriginalType;
        
        public List<UTypeInfo> GetBaseInterfaces()
        {
            if (this.baseInterfaces != null)
            {
                return this.baseInterfaces;
            }

            this.baseInterfaces = new List<UTypeInfo>();
            if (this.baseInterfaceWraps.Count < 1)
            {
                return this.baseInterfaces;
            }

            foreach (var item in this.baseInterfaceWraps)
            {
                UTypeInfo temp = item.InterfaceSymbol.ToTypeInfo();
                
                this.baseInterfaces.Add(temp);
            }

            return this.baseInterfaces;
        }

        public List<UTypeInfo> GetAllInterfaces()
        {
            if (this.allInterfaces != null)
            {
                return this.allInterfaces;
            }

            this.allInterfaces = new List<UTypeInfo>();
            if (this.allInterfaceSymbols.Count == 0)
            {
                return this.allInterfaces;
            }

            foreach (var item in allInterfaceSymbols)
            {
                var temp = item.ToTypeInfo();
                this.allInterfaces.Add(temp);
            }

            return this.allInterfaces;
        }


        public List<UTypeInfo> GetGenericArguments()
        {
            if (this.genericArguments != null)
            {
                return this.genericArguments;
            }

            this.genericArguments = new List<UTypeInfo>();
            if (this.GenericArgumentSymbols.Count == 0)
            {
                return genericArguments;
            }

            foreach (var argument in GenericArgumentSymbols)
            {
                this.genericArguments.Add(argument.ToTypeInfo());
            }

            return genericArguments;
        }

        public UTypeInfo GetGenericOriginalType()
        {
            if (this.GenericOriginalTypeSymbol == null)
            {
                return null;
            }

            if (this.genericOriginalType != null)
            {
                return this.genericOriginalType;
            }

            this.genericOriginalType = this.GenericOriginalTypeSymbol.ToTypeInfo();

            return this.genericOriginalType;
        }


        public void Parse()
        {
            var typeSymbol = this.sourceTypeSymbol;
            
            
            this.Name = typeSymbol.Name;
            this.FullName = typeSymbol.ToString();
        
            this.IsStatic = typeSymbol.IsStatic;
            this.IsSealed = typeSymbol.IsSealed;
            this.IsAbstract = typeSymbol.IsAbstract;
            this.IsTupleType = typeSymbol.IsTupleType;
            this.IsTypeError = typeSymbol.Kind == SymbolKind.ErrorType;
            this.Modifiers = typeSymbol.DeclaredAccessibility;
            
            if (typeSymbol is INamedTypeSymbol typeSymbol_)
            {
                this.IsGenericType = typeSymbol_.IsGenericType;
                this.IsSelfGenericType = false;
                this.GenericOriginalTypeSymbol = typeSymbol_.OriginalDefinition;
                this.GenericArgumentSymbols = typeSymbol_.TypeArguments.ToList();
            }
            else
            {
                this.IsSelfGenericType = true;
            }
            
            this.GetImplBaseType(typeSymbol);
            this.GetImplInterfaces(typeSymbol);
            this.GetPropertysFromType(typeSymbol);
            this.GetMethodsFromType(typeSymbol);
            this.AttributesParse(typeSymbol);
            
            this.IsInterface = (this.IsAbstract && this.BaseType == null);
        }

        private void AttributesParse(ITypeSymbol typeSymbol)
        {
            var items = typeSymbol.GetAttributes();
            foreach (var item in items)
            {
                this.Attributes.Add(item.AttributeClass.ToTypeInfo());
            }
        }

        private void GetImplBaseType(ITypeSymbol typeSymbol)
        {
            if (typeSymbol.BaseType != null)
            {
                this.BaseType = typeSymbol.BaseType.ToTypeInfo();
            }
        }

        private void GetImplInterfaces(ITypeSymbol typeSymbol)
        {
            this.allInterfaceSymbols = typeSymbol.AllInterfaces.ToList();
            
            var interfaces = typeSymbol.Interfaces;
            foreach (var _interface in interfaces)
            {
                this.baseInterfaceWraps.Add(new ImplInterfaceWrap(_interface)); 
            }
        }

        private void GetPropertysFromType(ITypeSymbol typeSymbol)
        {
            var members = typeSymbol.GetMembers();
            foreach (var item in members)
            {
                if (item is IPropertySymbol propertySymbol)
                {
                    var property = new UPropertyInfo();
                    property.Parse(propertySymbol);
                    this.Propertys.Add(property);
                }
            }
        }

        private void GetMethodsFromType(ITypeSymbol typeSymbol)
        {
            var members = typeSymbol.GetMembers();
            foreach (var item in members)
            {
                if (item is IMethodSymbol method)
                {
                    if (method.Name == ".ctor" || method.Name == ".cctor")
                    {
                        continue;
                    }

                    string methodFullName = method.ToString();
                    string methodName = methodFullName.Replace($"{this.FullName}.", string.Empty);
                    if (methodName.Contains(".set") || methodName.Contains(".get")) continue;

                    var methodInfo = new UMethodInfo(this, method);
                    methodInfo.Parse();

                    this.Methods.Add(methodInfo);
                }
            }
        }
    }
}